package com.bing.common.aspect;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.bing.common.annotation.RLog;
import com.bing.common.domain.entity.ReqLog;
import com.bing.common.domain.mapper.ReqLogRepository;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @Description 请求日志切面
 * @Author Bing
 * @Date 2024/10/15 8:47
 */
@Slf4j
@Aspect
@Component
@Order(1000)
public class RLogAspect {

    @Pointcut("@annotation(com.bing.common.annotation.RLog)")//切入点描述
    public void rLog() {}

    @Resource
    private ReqLogRepository reqLogRepository;

    private static final ThreadLocal<ReqLog> REQ_LOG_THREAD_LOCAL = new ThreadLocal();

    @Before("rLog()")
    public void before(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        if(!isRlog(signature.getMethod())){
            return;
        }
        ServletRequestAttributes attributes = (ServletRequestAttributes)
                RequestContextHolder.getRequestAttributes();

        HttpServletRequest request = attributes.getRequest();
        String uri = attributes.getRequest().getRequestURI();
        String method = attributes.getRequest().getMethod();
        // 获取header
        Map<String, String> headerMap = new HashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            String headerValue = request.getHeader(headerName);
            headerMap.put(headerName, headerValue);
        }
        //获取参数名称
        String queryParam = StrUtil.blankToDefault(JSONObject.toJSONString(parseParam( request)), "");
        String bodyParam = "";
        if(request.getContentType().contains("json")){
            bodyParam = StrUtil.blankToDefault(JSONObject.toJSONString(parseBody(joinPoint.getArgs())), "");
        }
        ReqLog reqLog = new ReqLog();
        reqLog.setId(IdUtil.getSnowflakeNextId()+"");
        reqLog.setUri(uri);
        reqLog.setMethod(method);
        reqLog.setContentType(request.getContentType());
        reqLog.setHeader(JSONObject.toJSONString(headerMap));
        reqLog.setQueryParam(queryParam);
        reqLog.setBodyParam(bodyParam);
        REQ_LOG_THREAD_LOCAL.set(reqLog);
    }

    @AfterReturning(value = "rLog()", returning = "result")
    public void after(JoinPoint joinPoint,Object result){
        try {
            MethodSignature signature = (MethodSignature)joinPoint.getSignature();
            if(!isRlog(signature.getMethod())){
                return;
            }
            ReqLog reqLog  = REQ_LOG_THREAD_LOCAL.get();
            reqLog.setResult(StrUtil.blankToDefault(JSONObject.toJSONString(result), ""));
            // 保存日志
            //reqLogRepository.save(reqLog);
        }finally {
            REQ_LOG_THREAD_LOCAL.remove();
        }
    }

    /**
     * 判断是否需要记录日志
     * @param method
     * @return
     */
    private boolean isRlog(Method method){
        RLog rLog = method.getAnnotation(RLog.class);
        return ObjectUtil.isNotNull(rLog);
    }

    /**
     * query参数
     * @return
     */
    private Map<String, Object> parseParam(HttpServletRequest request){
        // 获取query 参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        HashMap<String,Object> resultMap = new HashMap<>();
        if(CollUtil.isNotEmpty(parameterMap)){
            for(Map.Entry<String, String[]> entry: parameterMap.entrySet()){
                resultMap.put(entry.getKey(), entry.getValue()[0]);
            }
        }
        return resultMap;
    }

    /**
     * body参数
     * @param args
     * @return
     */
    private Map<String, Object> parseBody(Object[] args){
        HashMap<String,Object> resultMap = new HashMap<>();
        Arrays.stream(args).forEach(item ->{
            Map<String,Object>  map = BeanUtil.beanToMap( item);
            resultMap.putAll(map);
        });
        return resultMap;
    }
}